The Oracle
The oracle knows all, and it's kinda chatty. Is it telling you something?
nc chal.tuctf.com 30103
Recon
Upon connecting to the service, we're given a ciphertext and a menu to check the padding of the decrypted plaintext or enter a password. The assumption is that the password is the decrypted ciphertext we've been given.
As an initial guess, given the "check padding" function and that the ciphertexts are always a multiple of 128 bits, it seems like a CBC padding oracle with AES as the block cipher under the hood.
Welcome! The Oracle will see you now!
Your ciphertext is:
E3bVPSGYGadezlvcanGklpWiu0mnZWo9shKob1AiIOZLqO3Q96z96wWPfl0YBBA2
MENU:
1) Check padding
2) Enter password
1
Give me your input: E3bVPSGYGadezlvcanGklpWiu0mnZWo9shKob1AiIOZLqO3Q96z96wWPfl0YBBA2
Padding Valid
MENU:
1) Check padding
2) Enter password
22
Not a valid option
MENU:
1) Check padding
2) Enter password
2
What is the password? TEST
That's incorrect
MENU:
1) Check padding
2) Enter password
So the goal is to decrypt the ciphertext, then send the plaintext as the password.
Code
We've used the solid python-paddingoracle
framework from https://github.com/mwielgoszewski/python-paddingoracle to quickly build an exploit to get the password:
from pwn import *
from paddingoracle import BadPaddingException, PaddingOracle
from base64 import b64encode, b64decode
import time
class PadBuster(PaddingOracle):
def __init__(self, c, **kwargs):
super(PadBuster, self).__init__(**kwargs)
self.c = c
def oracle(self, data, **kwargs):
ct = b64encode(data)
while True:
try:
c.send("1\n")
c.send(ct + "\n")
c.readuntil("input:")
c.readuntil("Padding ")
response = c.readuntil("\n").strip()
logging.debug("GOT RESPONSE: %s" % response)
c.readuntil("password\n")
break
except Exception as e:
logging.exception(e)
continue
self.history.append(response)
if "Valid" in response:
logging.info("Got valid padding for %r" % ct)
return
logging.debug("Invalid padding for %r" % ct)
raise BadPaddingException
def get_ct():
c = remote("chal.tuctf.com", 30103)
r = c.readuntil("Your ciphertext is:\n\n")
print("< " + r)
ct = c.readuntil("\n").strip()
print("< " + c.readuntil("password\n\n"))
return ct, c
if __name__ == '__main__':
import logging
import sys
logging.basicConfig(level=logging.DEBUG)
ct, c = get_ct()
print("GOT CT: " + ct)
ct = b64decode(ct)
padbuster = PadBuster(c)
plain = padbuster.decrypt(ct, block_size=16)
print('Decrypted ciphertext: %s => %r' % (ct, plain))
Decrypted ciphertext: yx���R\x03\x1eC\x985����5R\x94x�h�\x93&t<\x1c���\x8b�T���3 ��\xb7>�o�Ȯ
=> bytearray(b'SUPERSECRETPASSWORDKEEPAWAY!\x04\x04\x04\x04')
[*] Closed connection to chal.tuctf.com port 30103
INFO:pwnlib.tubes.remote.remote.140695383547088:Closed connection to chal.tuctf.com port 30103
Solution
The ciphertext decrypts to the password SUPERSECRETPASSWORDKEEPAWAY!
, which gives us the flag.
Welcome! The Oracle will see you now!
Your ciphertext is:
NKQdIU2V1+2NtRueQIj5HWLfn3+H1+dYHXpg5mtX5QIlwg6j/wmjyyTxc/021e1t
MENU:
1) Check padding
2) Enter password
2
What is the password? SUPERSECRETPASSWORDKEEPAWAY!
That's it!
Congratulations!
Here's your flag:
TUCTF{D0nt_l3t_y0ur_s3rv3r_g1v3_f33db4ck}
Flag
TUCTF{D0nt_l3t_y0ur_s3rv3r_g1v3_f33db4ck}